<?php

// Debug

class Debug {

    // 代码跟踪信息
    static function trace($lev=5, $ex=[], $type='Debug'){
        global $_cbase;
        $exrow = []; $ertype = $type; $errmsg = '';
        if(!empty($ex) && is_array($ex)){ // $type=='Handler'
            $errmsg = $ex['errmsg']; $exrow = $ex; 
        }elseif(!empty($ex)){
            $errmsg = $ex->getMessage(); $excode = $ex->getCode(); 
            if($excode){ 
                $exmsg = Http::status($excode, 1);
                $ertype = $exmsg=='Unknow Error' ? "($excode)" : str_replace(' ','',$exmsg);
            }
            $exrow = ['file'=>$ex->getFile(), 'line'=>$ex->getLine()]; 
        }
        $info = ['type'=>$ertype, 'errmsg'=>$errmsg];
        // 
        $trace = empty($_cbase['_trace']) ? debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS,8) : $_cbase['_trace'];
        if(!empty($exrow)){ $trace = array_merge([$exrow], $trace); } 
        $tab = []; $cnt = 0; 
        foreach($trace as $k=>$row) {
            $file = empty($row['file']) ? '{no-file}' : $row['file']; $bf = basename($file);
            $line = empty($row['line']) ? -1 : $row['line'];
            $func = empty($row['function']) ? '' : '@'.$row['function'];
            if(in_array($bf, ['help.php','Debug.php'])){ continue; }
            #if(in_array($func, ['@abort','@trace'])){ continue; }
            $cnt++; if($cnt>$lev){ break; } 
            $tab[] = self::hidInfo($file)." ($line$func)";
        }
        // 
        return ['tab'=>$tab, 'info'=>$info];
    }

    // (变量+跟踪信息)打印-用于调试
    static function dump($vals, $flag='', $lev=3){
        if(!is_array($vals)){ $vals=[$vals]; }
        $tres = self::trace($lev); $trace = $tres['tab']; 
        foreach($trace as $k=>$row) {
            echo "\r\n$k >>> $row,<br> \n";
        }
        self::vdump($vals, $flag);
    }
    // 变量打印-用于调试
    static function vdump($vals, $flag=''){
        echo "\r\n<pre style='line-height:150%;'>"; 
        if($flag){ echo "[$flag]\n"; }
        foreach($vals as $val){ // print_r($var);
            if(count($vals)>1){ echo "<hr>"; }
            $exp = 1; $tp = "[".gettype($val)."] ";
            if(is_string($val)){
                $tp = "[string:".strlen($val)."] ";
            }elseif(is_array($val)){
                $tp = "[array:".count($val)."] "; 
            }elseif(is_object($val)){
                ;
            }else{ $exp = 0; }
            echo $tp;
            if($exp){
                echo str_replace(array('<','>'), array('&lt;','&gt;'), var_export($val,1));
            }else{
                var_dump($val);
            }
        }
        echo "</pre>\r\n";
    }

    # === (else) =======================================

    // 隐藏:根路径,表前后缀
    static function hidInfo($str='', $db=0){
        if(!$db){
            $a1 = [DIR_ROOT, dirname(DIR_PR), DIR_PR];
            $a2 = ['{root}', '{par1}', '{par2}'];
            $str = str_replace($a1, $a2, $str); 
        }else{
            require DIR_ROOT.'/cfgs/boot/dbcfg.php';
            $cdb = $_cfgs;
            $a2 = [" {pre}","{suf} ","`{pre}","{suf}`"];
            foreach(array('pre','suf') as $key){
                $fix = $cdb["db_{$key}fix"];
                if(!empty($fix)){
                    $str = str_replace(array(" $fix","$fix ","`$fix","$fix`"), $a2, $str);
                }
            }
        }
        return $str;
    }
    
    // 获取系统运行状态信息
    static function bugInfo($ustr=0){ // url-cut, run-used
        global $_cbase; $info = array(); 
        $info['ram'] = round(memory_get_usage()/1024/1024,3)."(MB)";
        $ut = microtime(1) - $_cbase['timer']; 
        $info['used'] = ($ut>1 ? number_format($ut,3).'(s)' : number_format($ut*1000,3).'(ms)');
        $info['vp'] = $_SERVER['REQUEST_URI'];
        $info['rp'] = uval('HTTP_REFERER','',0);
        foreach(array('vp','rp') as $k){
            $info[$k] = str_replace(array("'","\\"), array("`","#"), $info[$k]);
        }
        $info['ip'] = Basic::envIP();
        $info['ua'] = Basic::envUA();
        $info['req'] = $_SERVER['REQUEST_METHOD'];
        return $info;
    }

    # === logs : vist-record =======================================

    // 写日志
    static function log($act='', $exdata=[]){
        $rs = self::bugInfo(1);
        $exdata = is_array($exdata) ? json_encode($exdata,JSON_UNESCAPED_UNICODE|JSON_PRETTY_PRINT) : $exdata;
        $data = [date('Y-m-d H:i:s'), $rs['ip'], $rs['used'], strlen($exdata), $rs['ram'], $rs['rp'], $rs['vp']."#$rs[req]", $rs['ua']];
        $fp1 = ($act ? $act : 'log').date('-Y-md');
        $fps = DIR_DATA."/logs/$fp1.log";
        file_put_contents($fps, implode(" ,  ", $data)."\n$exdata\n\n", FILE_APPEND);
    }

    // 保存浏览记录
    static function vrSave($act='', $exdata='-'){
        $rs = self::bugInfo(1);
        $data = [date('Y-m-d H:i:s'), $rs['ip'], $rs['used'], $exdata, $rs['ram'], $rs['rp'], $rs['vp']."#$rs[req]", $rs['ua']];
        #$isBot = preg_match('/(Bot|Crawl|Spider|slurp|sohu-search|lycos|robozilla)/i', $rs['ua']);
        $fp1 = ($act ? $act : 'rv').date('-Y-md');
        $fps = DIR_DATA."/logs/$fp1.log";
        file_put_contents($fps, implode(" ,  ", $data)."\n", FILE_APPEND);
    }
    // 列表浏览记录
    static function vrList(){
        $base = DIR_DATA."/logs/";
        $dir = empty($_GET['dir']) ? '' : $_GET['dir']; if(strstr($dir,'./')) { die('.x.'); }
        $list = glob("$base/".($dir?"$dir/":'')."/*"); 
        $uri = $_SERVER['REQUEST_URI']; if(!strpos($uri,'?')){ $uri .= "?"; }
        echo "[$base".($dir?"$dir/":'')."/*]<br>\n";
        foreach($list as $fp){
            $bsname = basename($fp);
            if(is_dir($fp)){
                $size = '[dir]';
                $dirp = "$uri&act=list&dir=$bsname";
                $wname = 'vdir';
            }else{
                $snum = filesize($fp);
                $size = $snum<(1024*1024) ? round($snum/1024,2).'KB' : ' ~ ~ ~ ~ ~ ~ '.round($snum/(1024*1024),2).'MB';
                $dirp = "$uri&act=view&fp=$bsname".($dir?"&dir=$dir":'');
                $wname = 'vfp';
            }
            echo "<a href='$dirp' target='$wname'>$bsname</a> - $size<br>\n";
        }
    }
    // 查看浏览记录
    static function vrView(){
        $base = DIR_DATA."/logs/";
        $dir = empty($_GET['dir']) ? '' : $_GET['dir']; # 2022-06-07
        $fp = empty($_GET['fp']) ? '' : $_GET['fp']; if(strstr($fp,'./')) { die('.x.'); }
        $fp2 = "$base/".($dir?"$dir/":'')."$fp"; if(!file_exists($fp2)){ die('.x.'); }
        $vtxt = empty($_GET['vtxt']) ? '' : $_GET['vtxt'];
        echo "<pre>"; 
        if($vtxt){ echo file_get_contents($fp2)."</pre>"; return; }
        $arr = file($fp2); $data = []; $daip = [];
        foreach($arr as $row){
            $rs = explode(' ,  ', $row);
            preg_match("/[\d\.\:]+/", (empty($rs[1])?'-':$rs[1]), $itm1); //var_dump($itm1);
            if(!isset($itm1[0])){
                echo "$row\n"; continue;
            }else{ # ip
                $k21 = preg_match("/^xf\=/", $rs[1]) ? 'proxy-' : 'comm-';
                preg_match("/(\d+\.\d+\.\d+)\.(\d+)/", $itm1[0], $itm2);
                $k21 .= empty($itm2[1]) ? $itm1[0] : $itm2[1];
            } # ip (xf=::1;ra=192.168.0.1,192.168.0.1;cip=127.0.0.1)
            $isBot = preg_match('/(bot|spider)/i', $rs[7]); # HEAD|
            $isBad = preg_match('/(\.asp|\.jsp|\.ashx|\.asa|\/mytag|\/wp\-includes\/|POST|eval\()/i', $rs[6]);
            $row = str_replace([$rs[6]], ["<a href='{$rs[6]}' target='vpg'>{$rs[6]}</a>"], $row);
            $data[$k21][] = trim($row) . ($isBad?" --- _ISBAD_" : '') . ($isBot?" --- _ISBOT_" : '');
            if(!isset($daip[$k21])) { $daip[$k21] = $itm1[0]; }
        }
        ksort($data); $call = 0;
        foreach($data as $ipre=>$tab1){
            $cnt = count($tab1); $call += $cnt;
            echo "<b><a href='https://www.baidu.com/s?wd={$daip[$ipre]}' target='fip'>$ipre</a></b> --- $cnt\n";
            foreach($tab1 as $itm3){
                echo "    $itm3\n";
            }
        }
        echo("\n\n<b> ======= all views : $call</b>\n");
    }

}
